let aiDepth = 0
let concealTiles = 14
let playerCount = 4
let maxHu = 1
let playing = [true, false, false, false]

function createRound(name, dealer, render, end) {
	// Generate pile
	let pile = []
	for (let suit = 0; suit < 4; ++suit) {
		for (let tile = (suit === 3 ? 31 : (36 | suit)); tile > 3; tile -= 4) {
			for (let i = 0; i < 4; ++i) {
				pile.push(tile)
			}
		}
	}
	shuffleArray(pile)

	for (let i = 0; i < concealTiles; ++i) {
		pile.pop()
	}

	// Initialize player information
	let players = new Array(playerCount)
	for (let playerId = 0; playerId < playerCount; ++playerId) {
		let tiles = []
		for (let i = 0; i < 13; ++i) {
			tiles.push(pile.pop())
		}
		tiles.sort(compareTiles)

		players[playerId] = {
			hand: {
				melds: [],
				tiles: tiles,
			},
			discard: [],
			loss: 0,
			dealIn: [],
		}
	}

	// Compose round
	return {
		end: end,
		render: render,
		ended: false,
		name: name,
		pile: pile,
		players: players,
		toPlay: dealer,
		phase: PHASE_DRAW,
	}
}

function getNextPlayer(round, playerId) {
	for (let i = (playerId + 1) % playerCount; i != playerId; i = (i + 1) % playerCount) {
		if (!round.players[i].hu)
			return i
	}
	return playerId
}

function endRound(round) {
	for (let playerId = 0; playerId < playerCount; ++playerId) {
		let player = round.players[playerId]
		if (player.hu) {
			player.gain = player.hu.points * (playerCount - 1)
		} else {
			player.tenpai = tenpai(player.hand)
			player.gain = player.tenpai ? player.tenpai.points : 0
		}
	}

	round.ended = true
	console.log('End round')
	round.end()
	round.render()
}

function playPhase(round) {
	if (round.ended)
		return

	const toPlay = round.toPlay
	switch (round.phase) {
		case PHASE_DRAW:
			{
				// Phase draw
				if (!round.pile.length) {
					// Not enough tiles, round ends
					endRound(round)
					return
				}
				// Draw a tile
				round.players[toPlay].hand.tiles.push(round.pile.pop())
				for (let playerId = 0; playerId < playerCount; ++playerId) {
					round.players[playerId].choices = [[CHOICE_NONE]]
				}
				// Check tsumo
				if (hu(round.players[toPlay].hand, true)) {
					round.players[toPlay].choices.push([CHOICE_TSUMO])
				}
			}
			break
		case PHASE_CONCEAL:
			{
				// Phase conceal: conceal melds
				for (let playerId = 0; playerId < playerCount; ++playerId) {
					round.players[playerId].choices = [[CHOICE_NONE]]
				}
				for (const meld of checkConcealedMelds(round.players[toPlay].hand.tiles)) {
					// Check concealed melds
					round.players[toPlay].choices.push(meld)
				}
				for (const meld of checkAddedMelds(round.players[toPlay].hand.tiles, round.players[toPlay].hand.melds)) {
					// Check added melds
					round.players[toPlay].choices.push(meld)
				}
			}
			break
		case PHASE_DISCARD:
			{
				// Phase discard: discard a tile
				for (let playerId = 0; playerId < playerCount; ++playerId) {
					round.players[playerId].choices = [[CHOICE_NONE]]
				}
				round.players[toPlay].choices = sortAndUnique(round.players[toPlay].hand.tiles).map(tile => [CHOICE_DISCARD, tile])
			}
			break
		case PHASE_MELD:
			{
				// Phase meld: hu or meld
				round.players[toPlay].choices = [[CHOICE_NONE]]
				const discarded = round.discarded
				const nextPlayer = getNextPlayer(round, toPlay)
				for (let playerId = 0; playerId < playerCount; ++playerId)if (playerId !== toPlay && !round.players[toPlay].hu) {
					let hand = structuredClone(round.players[playerId].hand)
					let choices = [[CHOICE_NONE]]
					for (const meld of checkMelds(hand.tiles, discarded)) {
						// Check melds
						if (meld[0] !== CHOICE_CHOW || playerId === nextPlayer)
							choices.push(meld)
					}
					// Check hu
					hand.tiles.push(discarded)
					if (hu(hand, false)) {
						choices.push([CHOICE_HU])
					}
					round.players[playerId].choices = choices
				}
			}
			break
	}

	for (let playerId = 0; playerId < playerCount; ++playerId)if (round.players[playerId].hu) {
		round.players[playerId].choices = [[CHOICE_NONE]]
	}

	// Auto make choice for players
	for (let playerId = 0; playerId < playerCount; ++playerId) {
		if (round.players[playerId].choices.length === 1) {
			round.players[playerId].choice = round.players[playerId].choices[0]
		} else {
			round.players[playerId].choice = undefined
		}
	}

	let needAI = false
	for (let playerId = 0; playerId < playerCount; ++playerId) {
		if (!playing[playerId] && round.players[playerId].choices.length > 1) {
			needAI = true
		}
	}
	if (needAI) {
		round.render()
		setTimeout(() => {
			for (let playerId = 0; playerId < playerCount; ++playerId) {
				if (!playing[playerId] && round.players[playerId].choices.length > 1) {
					round.players[playerId].choice = aiChoice(round, playerId)
				}
			}
			makeChoice(round)
		}, AI_DELAY)
	} else {
		makeChoice(round)
	}
}

function handleHu(round, hs) {
	for (const h of hs) {
		const u = hu(h[2], h[1])
		round.players[h[0]].hu = u
		if (h[1]) {
			for (let playerId = 0; playerId < playerCount; ++playerId)if (playerId !== h[0]) {
				round.players[playerId].loss += u.points
				round.players[playerId].dealIn.push([u.points, true])
			}
		} else {
			round.players[h[3]].loss += u.points * (playerCount - 1)
			round.players[h[3]].dealIn.push([u.points * (playerCount - 1), false])
		}
	}
	let huCount = 0
	for (const player of round.players) {
		if (player.hu) {
			++huCount
		}
	}
	if (huCount < maxHu) {
		round.toPlay = getNextPlayer(round, round.toPlay)
		round.phase = PHASE_DRAW
		playPhase(round)
	} else {
		endRound(round)
	}
}

function makeChoice(round) {
	// Check if all players have made their choices
	{
		for (let playerId = 0; playerId < playerCount; ++playerId) {
			if (!round.players[playerId].choice) {
				round.render()
				return
			}
		}
	}

	const toPlay = round.toPlay
	switch (round.phase) {
		case PHASE_DRAW:
			{
				// Add tsumo
				if (round.players[toPlay].choice[0] === CHOICE_TSUMO) {
					handleHu(round, [[toPlay, true, round.players[toPlay].hand]])
					return
				}
				round.phase = PHASE_CONCEAL
			}
			break
		case PHASE_CONCEAL:
			{
				// Add concealed/added kongs
				if (round.players[toPlay].choice[0] !== CHOICE_NONE) {
					// Has meld
					const choice = round.players[toPlay].choice
					for (const tile of choice[3]) {
						removeElementFromArray(round.players[toPlay].hand.tiles, tile)
					}
					if (choice[0] === CHOICE_ADDED_KONG) {
						for (let meld of round.players[toPlay].hand.melds) if (meld[1] === choice[1]) {
							meld[0] = CHOICE_ADDED_KONG
							for (const tile of choice[3]) {
								meld[3].push(tile)
							}
							break
						}
					} else {
						round.players[toPlay].hand.melds.push(structuredClone(choice))
					}
					round.phase = PHASE_DRAW
				} else {
					round.phase = PHASE_DISCARD
				}
			}
			break
		case PHASE_DISCARD:
			{
				// Add discard
				const discarded = round.players[toPlay].choice[1]
				round.discarded = discarded
				round.players[toPlay].discard.push(discarded)
				let tiles = round.players[toPlay].hand.tiles
				removeElementFromArray(tiles, discarded)
				tiles.sort(compareTiles)
				round.players[toPlay].tenpaiHint = tenpai(round.players[toPlay].hand) ? true : false
				round.phase = PHASE_MELD
			}
			break
		case PHASE_MELD:
			{
				// Add hu or meld
				if (round.players.findIndex(player => player.choice[0] === CHOICE_HU) !== -1) {
					// Has hu
					let hs = []
					for (let playerId = 0; playerId < playerCount; ++playerId)if (round.players[playerId].choice[0] === CHOICE_HU) {
						let hand = round.players[playerId].hand
						hand.tiles.push(round.discarded)
						hs.push([playerId, false, hand, toPlay])
					}
					handleHu(round, hs)
					return
				} else {
					let choiceType = CHOICE_NONE, toMeld
					for (let playerId = getNextPlayer(round, toPlay); playerId !== toPlay; playerId = getNextPlayer(round, playerId)) {
						if (round.players[playerId].choice[0] > choiceType) {
							choiceType = round.players[playerId].choice[0]
							toMeld = playerId
						}
					}
					if (choiceType !== CHOICE_NONE) {
						// Has meld
						const choice = round.players[toMeld].choice
						for (const tile of choice[3]) {
							removeElementFromArray(round.players[toMeld].hand.tiles, tile)
						}
						round.players[toMeld].hand.melds.push(structuredClone(choice))

						round.players[toPlay].discard.pop()

						round.toPlay = toMeld
						round.phase = (choiceType === CHOICE_KONG) ? PHASE_DRAW : PHASE_CONCEAL
					} else {
						// No hu or meld
						round.toPlay = getNextPlayer(round, round.toPlay)
						round.phase = PHASE_DRAW
					}
				}
			}
			break
	}
	playPhase(round)
}

function setChoice(round, playerId, choice) {
	round.players[playerId].choice = choice
	makeChoice(round)
}